<?php
/*
tiesh.com
v1.00
bach
2017-04-24

    add($demand,$data{($content)})
    respond($demand,$data{$code,$content})
    check($demand,$data{$code,($content)})
    query($demand,$data)
*/
namespace app\common\model;
use think\Model;
// use think\Db;
// Db::listen(function($sql, $time, $explain){
//     // 记录SQL
//     echo $sql. ' ['.$time.'s]';
//     // 查看性能分析结果
//     dump($explain);
// });
class Token extends Model {


    protected $autoCheckFields = false;//关闭检测字段

    private function getRandChar($length) {
        $str = null;
        $strPol = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789abcdefghijklmnopqrstuvwxyz";
        $max = strlen($strPol) - 1;
        for ($i = 0; $i < $length; $i++) {
            $str.=$strPol[rand(0, $max)]; //rand($min,$max)生成介于min和max两个数之间的一个随机整数
        }
        return $str;
    }
    private function newCode($type,$length) {
        switch ($type) {
            case 'char':
            $code = $this->getRandChar($length);
            break;
            case 'int':
            $code = "";
            while( strlen($code) < $length){
                $code.= rand(0,9);
            }
            break;
        }
        return $code;
    }

    var $config;
    //配置文件
    public function __construct(){
        // $path = C("CONFIG_PATH")."/token.demand.config.php";
        $config = getConfig('token',1);//json_decode(trim(substr(file_get_contents($path), 15)),1);
        $this->config = $config;
        // //dump($config);
    }

    //生成token
    public function add($demand,$data = null) {
        $config = $this->config;
        if(!$config){
            return ["errormsg"=>"config empty"];
        }
        //检查demand
        if(!isset($config[$demand])){
            return ["errormsg"=>"demand empty"];
        }

        $config = $this->config[$demand];
        //dump($config);
        $content = null;
        $content_respond = isset($config['content_respond'])?$config['content_respond']:null;
        if(!$content_respond){
            $content = $data['content'];
            if(!$content) return ["errormsg"=>"参数错误"];
            $content_reg = $config['content_reg'];
            if($content_reg){
                $reg_result = preg_match($content_reg, $content,$match);
                if(!$match){
                    return ['errormsg'=>'token content incorrect','data'=>$content];
                } 
            }
        }
        //检查冷却
        $cooldown = $config['cooldown'];
        if(!$cooldown){
            return ["errormsg"=>"参数错误"];
        }
        $cooldown = explode(',',$cooldown);
        //dump($cooldown);
        $where = [];
        if(!$content_respond && $content){//筛选条件
            $where['content'] = $content;
        }
        // $class = (int)$config[id];
        // if(!$class){
            // $ret = array(state=>0,errormsg=>'class id empty',data=>array('class'=>$class));
            // return($ret);
        // }
        // dump($where);
        $limit = $cooldown[2]?$cooldown[2]:1;$order = "token_id DESC";$field=array('create_time');
        // $where[] = "CLASS = $class";
        $res = db("token_".$demand)//查询有限数量内个token，分别检查他们的create_time
        ->field($field)
        ->limit($limit)
        ->order($order)
        ->where($where)
        ->select();
        //echo M()->getLastSql();
        //dump($res);
        if(count($res)){
            $timeExpired = time() - $res[0]['create_time'];
            //echo timeExpired.$timeExpired." ".$cooldown[0]."<BR>";
            if( $timeExpired < $cooldown[0] ){
                $remain = $cooldown[0] - $timeExpired;
                return ['state'=>0,'errormsg'=>"请稍后再试",'data'=>array('remain'=>$remain)];
            }
            if(isset($res[$cooldown[2]-1])){
                $timeExpired = time() - $res[$cooldown[2]-1]['create_time'];
                if( $timeExpired < $cooldown[1] ){
                    $remain = $cooldown[1] - $timeExpired;
                    return ['state'=>0,'errormsg'=>"操作太频繁,请稍后再试",'data'=>array('remain'=>$remain)];
                }
            }
        }
        //检查格式
        $code = explode(",",$config['code']);
        if(!$code){
            return array('state'=>0,'errormsg'=>"参数错误");
        }
        $code = $this->newCode($code[0],$code[1]);
        $user = new User;
        $userId = $user->getUserId();
        $code_save = md5($code);
        $add = array(
            'checked_times' => 0,
            'creator'       => $userId,
            'code'          => $code_save,
            'content'       => $content,
            'create_time'   => time(),
            // 'class'=>$class,
            // 'content'=>$content,
            );
        $res = db('token_'.$demand)->insert($add);
        if(!$res){
            return array('state'=>0,'errormsg'=>"database save error");
        }
        $remain = $cooldown[0];
        //检验成功 生成
        return array('state'=>1,'data'=>array('code'=>$code,'remain'=>$remain));
    }
    //创建token别名
    // public function save($demand,$data) {
        // return $this->new($demand,$data);
    // }

    //token响应
    public function respond($demand, $data) {
        $config = $this->config;
        $code = $data['code'];
        if(!$code){
            return array('state'=>0,'errormsg'=>'code missing');
        }
        $content = $data['content'];
        if(isset($config['content_reg'])){
            $content_reg = $config['content_reg'];
            $reg_result = preg_match($content_reg, $content,$match);
            // //echo "match";
            // //dump($content_reg);
            // //dump($content);
            // //dump($match);
            if(!$match){
                return array('state'=>0,'errormsg'=>'content incorrect');
            } 
        }
        if(!$content){
            return array('state'=>0,'errormsg'=>'need content');
        }
        if(!$this->config){
            $ret = array('state'=>0,'errormsg'=>'config empty');
            return($ret);
        }
        //dump($config);
        //检查demand
        if(!isset($config[$demand])){
            $ret = array('state'=>0,'errormsg'=>'demand incorrect');
            return($ret);
        }
        $config = $config[$demand];
        //检查content
        if(!isset($config['content_respond']) || !$config['content_respond']){
            $ret = array('state'=>0,'errormsg'=>'this demand cannot be responded');
            return($ret);
        }
        $content_respond = $config['content_respond'];
        //开始查询code
        // $class = (int)$config[id];
        $code = md5($code);
        $where['code'] = $code;
        // $where[] = "CLASS = $class";
        $limit = 1;$order = "token_id DESC";$field=array('content,create_time,token_id');
        $res = db('token_'.$demand)
        ->field($field)
        // ->limit($limit)
        ->order($order)
        ->where($where)
        ->find();
        //echo M()->getLastSql();
        if(!$res){
            return array('state'=>0,'errormsg'=>'code error','action'=>'token respond');
        }
        //检查响应过了不
        if($res['content']){
            return array('state'=>0,'errormsg'=>'code responded');
        }
        //检查过期了不
        $expire = (int)$config['expire'];
        if(!$expire){
            return array('state'=>0,'errormsg'=>'expire config incorrect');
        }
        //dump(time() - $res[create_time]);
        if(time() - $res['create_time'] > $expire){
            return array('state'=>0,'errormsg'=>'code expired');
        }
        $tokenId = $res['token_id'];
        $where = "token_id = '$tokenId'";
        $user = new User;
        $userId = $user->getUserId();
        $save = array(
            "content"=>$content,
            "responder"=>$userId,
            "respond_time"=>time(),
            );
        $res = db('token_'.$demand)->where($where)->update($save);
        //echo M()->getLastSql();
        //dump($res);
        return array('state'=>1,'errormsg'=>'respond success');

    }

    //检查token
    public function check($demand, $data) {
        $code = $data['code'];
        if(!$code){
            return array('state'=>0,'errormsg'=>'code missing');
        }
        if(!$this->config){
            $ret = array('state'=>0,'errormsg'=>'config empty');
            return($ret);
        }
        $config = $this->config[$demand];
        //dump($config);
        //检查demand
        if(!$config){
            $ret = array('state'=>0,'errormsg'=>'demand incorrect');
            return($ret);
        }
        if(isset($config['content_check'])){
            if(!isset($data['content'])){
                $ret = array('state'=>0,'errormsg'=>'need content');
            }
            $content = $data['content'];
            $content_reg = $config['content_reg'];
            if($content_reg){
                $reg_result = preg_match($content_reg, $content,$match);
                if(!$match){
                    return array('state'=>0,'errormsg'=>'content incorrect');
                } 
            }
            $where['content'] = $content;
            $expire = (int)$config['expire'];
            $t = time()-$expire;
            // $class = (int)$config[id];
            //检查次数
            $p = config('database.prefix');
            // $sql = " UPDATE `".$p."token_$demand` SET `CHECKED_TIMES`=CHECKED_TIMES+1 WHERE `CONTENT` = '$content' AND `STATE` is null AND `CLASS` = $class AND CREATE_TIME > $t ";
            $sql = " UPDATE `".$p."token_$demand` SET `checked_times`=checked_times+1 WHERE `content` = '$content' AND `state` is null AND create_time > $t ";
            $res = db()->execute($sql);
            // die();
        }
        //开始查询code
        // $class = (int)$config[id];
        //dump($code);
        $code = md5($code);
        $where["code"] = $code;
        // $where[] = "CLASS = $class";
        $limit = 1;
        $order = "token_id DESC";
        $field=array('content,create_time,token_id,check_time,creator,checked_times');
        $res = db('token_'.$demand)
        ->field($field)
        // ->limit($limit)
        ->order($order)
        ->where($where)
        ->find();
        // echo M()->getLastSql();
        if(!$res){
            return array('state'=>0,'errormsg'=>'code error','action'=>'token check');
        }
        $content_checked_most = isset($config['content_checked_most'])?$config['content_checked_most']:null;
        $checked_times = ($res['checked_times']?$res['checked_times']:0) + 1;
        if($content_checked_most && ($checked_times>$content_checked_most)){
            return array('state'=>0,'errormsg'=>'check too much');
        }
        //检查check过了不
        if($res['check_time']){
            return array('state'=>0,'errormsg'=>'code checked');
        }
        $user_id = $res['creator'];
        //检查响应过了不
        $content = $res['content'];
        if(!$content){
            return array('state'=>0,'errormsg'=>'not responded');
        }
        //检查过期了不
        $expire = (int)$config['expire'];
        if(!$expire){
            return array('state'=>0,'errormsg'=>'expire config incorrect');
        }
        //dump(time() - $res[create_time]);
        if(time() - $res['create_time'] > $expire){
            return array('state'=>0,'errormsg'=>'code expired');
        }
        $tokenId = $res['token_id'];
        $where = "TOKEN_ID = '$tokenId'";
        $user = new User;
        $userId = $user->getUserId();
        $save = array(
            "state"=>1,
            "checker"=>$userId,
            "check_time"=>time(),
            );
        $res = db('token_'.$demand)->where($where)->update($save);
        //echo M()->getLastSql();
        if(!$res){
            return array('state'=>0,'errormsg'=>'database save incorrect');

        }
        //dump($res);
        return array('state'=>1,'data'=>array('content'=>$content,'user_id'=>$user_id));
    }
    //检查token
    public function query($demand, $data) {
        $code = $data['code'];
        if(!$code){
            return array('state'=>0,'errormsg'=>'code missing');
        }
        if(!$this->config){
            $ret = array('state'=>0,'errormsg'=>'config empty');
            return($ret);
        }
        $config = $this->config[$demand];
        //dump($config);
        //检查demand
        if(!$config){
            $ret = array('state'=>0,'errormsg'=>'demand incorrect');
            return($ret);
        }
        if(isset($config['content_check'])){
            if(!isset($data['content'])){
                $ret = array('state'=>0,'errormsg'=>'need content');
            }
            $content = $data['content'];
            $content_reg = $config['content_reg'];
            if($content_reg){
                $reg_result = preg_match($content_reg, $content,$match);
                if(!$match){
                    return array('state'=>0,'errormsg'=>'content incorrect');
                } 
            }
            $where['content'] = $content;
            $expire = (int)$config['expire'];
            $t = time()-$expire;
            // $class = (int)$config[id];
            //检查次数
            $p = config('database.prefix');
            // $sql = " UPDATE `".$p."token_$demand` SET `CHECKED_TIMES`=CHECKED_TIMES+1 WHERE `CONTENT` = '$content' AND `STATE` is null AND `CLASS` = $class AND CREATE_TIME > $t ";
            $sql = " UPDATE `".$p."token_$demand` SET `checked_times`=checked_times+1 WHERE `content` = '$content' AND `state` is null AND create_time > $t ";
            $res = db()->execute($sql);
            // die();
        }
        //开始查询code
        // $class = (int)$config[id];
        //dump($code);
        $code = md5($code);
        $where["code"] = $code;
        // $where[] = "CLASS = $class";
        $limit = 1;
        $order = "token_id DESC";
        $field=array('content,create_time,token_id,check_time,creator,checked_times');
        $res = db('token_'.$demand)
        ->field($field)
        // ->limit($limit)
        ->order($order)
        ->where($where)
        ->find();
        // echo M()->getLastSql();
        if(!$res){
            return array('state'=>0,'errormsg'=>'code error','action'=>'token check');
        }
        $content_checked_most = isset($config['content_checked_most'])?$config['content_checked_most']:null;
        $checked_times = ($res['checked_times']?$res['checked_times']:0) + 1;
        if($content_checked_most && ($checked_times>$content_checked_most)){
            return array('state'=>0,'errormsg'=>'check too much');
        }
        //检查check过了不
        if($res['check_time']){
            return array('state'=>0,'errormsg'=>'code checked');
        }
        $user_id = $res['creator'];
        //检查响应过了不
        $content = $res['content'];
        if(!$content){
            return array('state'=>0,'errormsg'=>'not responded');
        }
        //检查过期了不
        $expire = (int)$config['expire'];
        if(!$expire){
            return array('state'=>0,'errormsg'=>'expire config incorrect');
        }
        //dump(time() - $res[create_time]);
        if(time() - $res['create_time'] > $expire){
            return array('state'=>0,'errormsg'=>'code expired');
        }
        //dump($res);
        return array('state'=>1,'data'=>array('content'=>$content,'user_id'=>$user_id));
    }
}
